<template>
  <div class="tts-container">
    <div class="voice-container">
      <!-- 头部区域：搜索和添加声音的按钮-->
      <div class="header">
        <h2 class="title">语音库</h2>
        <div class="search-area">
          <a-input v-model:value="searchQuery" placeholder="搜索声音名称" class="search-input" :style="{ fontSize: '14px' }">
            <template #prefix>
              <search-outlined style="color: #bfbfbf" />
            </template>
          </a-input>
          <!-- 添加声音按钮 -->
          <a-button type="primary" @click="showModal" class="add-button">
            <template #icon><plus-outlined /></template>
            添加声音
          </a-button>
        </div>
      </div>
      <!-- 分类过滤栏 -->
      <div class="filter-bar">
        <!-- 全部分类的按钮 -->
        <a-button :type="activeType === 'all' ? 'primary' : 'default'" class="filter-button"
          @click="() => setActiveType('all')">全部分类</a-button>
        <!-- 其他类型的分类按钮 -->
        <a-button v-for="type in voiceTypes" :key="type.value" :type="activeType === type.value ? 'primary' : 'default'"
          class="filter-button" @click="() => setActiveType(type.value)">{{ type.label }}</a-button>
      </div>
      <!-- 使用 a-card 组件包装表格-->
      <a-card class="table-card">
        <!-- 
          声音列表表格组件
          :columns - 定义表格列的配置
          :data-source - 表格数据源
          :pagination="false" - 禁用表格自带的分页
        -->
        <a-table :columns="columns" :data-source="voiceList" :pagination="false">
          <!-- 自定义表格单元格渲染 column : 当前列的配置信息，record : 当前行的数据对象 -->
          <template #bodyCell="{ column, record }">
            <!-- 声音类型列：显示带颜色的标签 -->
            <template v-if="column.key === 'voiceType'">
              <!-- 根据声音类型显示不同颜色的标签 -->
              <a-tag :color="getTypeColor(record.voiceType)">{{ getTypeLabel(record.voiceType) }}</a-tag>
            </template>
            <!-- 试听按钮列：显示播放图标 -->
            <template v-if="column.key === 'play'">
              <div class="play-action">
                <!-- 播放按钮，使用链接样式 -->
                <a-button type="link" class="action-btn" @click="handlePlayAudio(record)">
                  <template #icon>
                    <pause-circle-outlined v-if="isPlaying && currentPlayingId === record.voiceId" class="play-icon" />
                    <play-circle-outlined v-else class="play-icon" />
                  </template>
                </a-button>
              </div>
            </template>

            <!-- 操作列：显示编辑和删除按钮 -->
            <template v-if="column.key === 'action'">
              <div class="action-group">
                <!-- 编辑按钮 -->
                <a-button
                  type="link"
                  @click="editVoice(record)"
                  class="action-btn"
                  :disabled="record.voiceType === 'system'"
                  :title="record.voiceType === 'system' ? '系统声音不可编辑' : ''"
                >
                  <template #icon> <edit-outlined class="edit-icon" /></template>
                </a-button>
                <!-- 删除按钮 -->
                <a-button
                  type="link"
                  @click="deleteVoice(record)"
                  class="action-btn"
                  :disabled="record.voiceType === 'system'"
                  :title="record.voiceType === 'system' ? '系统声音不可删除' : ''"
                >
                  <template #icon><delete-outlined class="delete-icon" /> </template>
                </a-button>
              </div>
            </template>
          </template>
        </a-table>
      </a-card>
      <!-- 页脚区域：分页信息、分页组件 -->
      <div class="pagination-area">
        <div class="total-info">
          共 {{ total }} 条声音
        </div>
        <a-pagination v-model:current="current" :total="total" :page-size="pageSize" @change="handlePageChange" />
      </div>
      <!-- 添加和编辑声音弹窗 -->
      <a-modal v-model:open="modalVisible" :title="form.voiceId ? '编辑声音' : '添加声音'" @ok="handleOk" @cancel="handleCancel"
        :confirmLoading="confirmLoading" class="ant-modal" okText="确定" cancelText="取消">
        <a-form ref="formRef" :model="form" :rules="rules" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
          <a-form-item label="声音名称" name="voiceName">
            <a-input v-model:value="form.voiceName" placeholder="请输入声音名称" />
          </a-form-item>
          <a-form-item name="voiceNote" label="声音备注">
            <template #label>
              声音备注
              <a-tooltip placement="top">
                <template #title>
                  添加对该声音的说明或备注信息
                </template>
                <QuestionCircleOutlined class="help-icon" />
              </a-tooltip>
            </template>
            <a-textarea v-model:value="form.voiceNote" placeholder="请输入声音备注" :maxLength="20" :rows="4"
              :show-count="{ formatter: ({ count, maxLength }) => `${count} / ${20}` }" />
          </a-form-item>
          <a-form-item label="上传音频" name="file" v-if="!form.voiceId">
            <a-upload-dragger v-model:fileList="form.file" :before-upload="beforeUpload"
              accept="audio/mp3,audio/mpeg,audio/wav" :max-count="1">
              <p class="upload-icon">
                <upload-outlined />
              </p>
              <p>点击或拖拽文件到这里上传</p>
              <p class="upload-tip">支持 MP3、WAV 格式，最大 50MB</p>
            </a-upload-dragger>
          </a-form-item>
        </a-form>
      </a-modal>
    </div>
  </div>

</template>
<script setup>
  import { ref, reactive, onUnmounted, onMounted, watch } from 'vue';
  import { message, Modal } from 'ant-design-vue';
  import { useRoute } from 'vue-router';
  import { getVoiceList, addVoice, editVoiceData, deleteVoiceData, getCloneStatus, reportError } from '@/api/VoiceLibrary';
  import {
    SearchOutlined,
    PlusOutlined,
    PlayCircleOutlined,
    EditOutlined,
    DeleteOutlined,
    UploadOutlined,
    PauseCircleOutlined,
    QuestionCircleOutlined
  } from '@ant-design/icons-vue';
  const current = ref(1);  // 当前页码
  const pageSize = ref(7);  // 每页显示的数据条数
  const total = ref(0);  // 显示声音的总条数
  // 声音列表数据，用于表格展示的模拟数据，之后是后端传输过来
  const voiceList = ref([]);
  // 获取声音列表数据的方法
  const fetchVoiceList = async (type = 'all', pageNo = current.value, size = pageSize.value, keyword = '') => {
    try {
      const res = await getVoiceList({ type, pageNo, pageSize: size, keyword });
      voiceList.value = res.data.list;
      total.value = res.data.totalCount;
    } catch (error) {
      message.error('请检查网络连接',1);
    }
  };
  // 在组件挂载时获取数据
  const route = useRoute();
  onMounted(async () => {
    await fetchVoiceList();
    // 检查路由参数，如果需要则自动打开添加声音弹窗
    if (route.query.openAddModal) {
      showModal();
    }
  });
  const searchQuery = ref('');  // 搜索关键字
  // 监听搜索关键字变化
  watch(searchQuery, async (newValue) => {
    await fetchVoiceList(activeType.value, current.value, pageSize.value, newValue);
  });

  const modalVisible = ref(false);  // 控制模态框的显示和隐藏
  //显示添加声音弹窗的方法
  const showModal = () => {
    resetForm();
    form.voiceId = '';
    form.voiceType = 'clone';
    modalVisible.value = true;
  };

  const voiceTypes = [  // 声音类型列表，用于分类过滤和表单选择，再下拉框中直接使用
    { label: '系统声音', value: 'system' },
    { label: '克隆声音', value: 'clone' }
  ];
  const activeType = ref('all');  // 当前选中的声音类型过滤条件，用于对按钮颜色展示
  //进行分类过滤的方法，给后端传输类型
  const setActiveType = (type) => {
    activeType.value = type;
    fetchVoiceList(type, current.value, pageSize.value, searchQuery.value);
  };

  // 表格列配置，定义表格的列结构和属性,自己定义
  const columns = [
    {
      title: '声音 ID',
      dataIndex: 'voiceId',
      key: 'voiceId'
    },
    {
      title: '名称',
      dataIndex: 'voiceName',
      key: 'voiceName'
    },
    {
      title: '声音备注',
      dataIndex: 'voiceNote',
      key: 'voiceNote'
    },
    {
      title: '声音类型',
      key: 'voiceType'
    },
    {
      title: '试听',
      key: 'play',
      align: 'center'
    },
    {
      title: '操作',
      key: 'action',
      align: 'center'
    }
  ];

  // 根据声音类型获取颜色和对应标签的方法，不用管
  const getTypeColor = (type) => {
    const typeColors = {
      system: 'blue',
      clone: 'green'
    };
    return typeColors[type] || 'blue';
  };
  const getTypeLabel = (type) => {
    const type_obj = voiceTypes.find(t => t.value === type);
    return type_obj ? type_obj.label : '';
  };
  //试听按钮部分
  const isPlaying = ref(false);  // 控制播放按钮的图标切换
  const currentPlayingId = ref(null);
  const audioPlayer = ref(null);
  // 添加音频播放处理方法
  const handlePlayAudio = async (record) => {
    try {
      if (!record.outputUrl) {
        message.warning('音频文件不存在',1);
        return;
      }
      // 如果已经有正在播放的音频且不是当前点击的音频，先停止它
      if (audioPlayer.value && currentPlayingId.value && currentPlayingId.value !== record.voiceId) {
        audioPlayer.value.pause();
      }

      if (!audioPlayer.value) {
        audioPlayer.value = new Audio();
        audioPlayer.value.onended = () => {
          isPlaying.value = false;
          currentPlayingId.value = null;
        };
      }

      // 如果点击的是当前正在播放的音频
      if (currentPlayingId.value === record.voiceId) {
        if (isPlaying.value) {
          audioPlayer.value.pause();
          isPlaying.value = false;
        } else {
          await audioPlayer.value.play();
          isPlaying.value = true;
        }
        return;
      }
      // 播放新的音频
      audioPlayer.value.src = record.outputUrl;
      await audioPlayer.value.play();
      isPlaying.value = true;
      currentPlayingId.value = record.voiceId;
    } catch (error) {
      message.error('音频播放失败',1);
      console.error('播放失败:', error);
      await reportError(record.voiceId, error);
    };
  };
  // 组件卸载时清理音频播放器
  onUnmounted(() => {
    if (audioPlayer.value) {
      audioPlayer.value.pause();
      audioPlayer.value = null;
    }
  });
  //显示编辑声音弹窗的方法
  const editVoice = (record) => {
    resetForm();
    form.voiceId = record.voiceId;
    form.voiceName = record.voiceName;
    form.voiceNote = record.voiceNote;
    form.voiceType = record.voiceType;
    modalVisible.value = true;
  };
  //取消添加或编辑声音的方法
  const handleCancel = () => {
    modalVisible.value = false;
    resetForm();
  };
  //重置表单
  const resetForm = () => {
    formRef.value?.resetFields();
    form.voiceId = '';
    form.voiceName = '';
    form.voiceNote = '';
    form.voiceType = '';
    form.file = null;
  };
  // 删除声音的方法  
  const deleteVoice = (record) => {
    Modal.confirm({
      title: '确定要删除这个声音吗?',
      content: `将要删除声音: ${record.voiceName}`,
      okText: '确定',
      okType: 'danger',
      cancelText: '取消',
      async onOk() {
        try {
          await deleteVoiceData(record.id);
          message.success('删除成功',1);
          await fetchVoiceList(activeType.value, current.value, pageSize.value, searchQuery.value);
        } catch (error) {
          message.error('删除失败',1);
        }
      },
    });
  };
  //页数改变时触发的方法，给后端传输页码
  const handlePageChange = (pageNo) => {
    current.value = pageNo;
    fetchVoiceList(activeType.value, pageNo, pageSize.value, searchQuery.value);
  };
  //表单弹窗部分
  const formRef = ref();  // 表单引用，用于表单验证和重置
  const confirmLoading = ref(false);  // 控制确认按钮的加载状态
  // 表单数据对象，用于添加和编辑声音时的数据存储，之后是传输给后端
  const form = reactive({
    voiceId: '',
    voiceName: '',
    voiceNote: '',
    voiceType: '',
    file: null
  });
  //校验规则
  const rules = {
    voiceName: [
      { required: true, message: '请输入声音名称' },
      { min: 2, max: 8, message: '名称长度必须在2到8个字符之间' },
      {
        validator: async (_, value) => {
          if (!value) return Promise.resolve();
          const res = await getVoiceList({ type: 'all', keyword: value });
          const exists = res.data.list.some(item => 
            item.voiceName === value && item.voiceId !== form.voiceId
          );
          if (exists) {
            return Promise.reject('声音名称已存在');
          }
          return Promise.resolve();
        }
      }
    ],
    voiceNote: [{ required: true, message: '请输入声音备注内容' }],
    file: [{
      required: true,
      validator: (_, value) =>
        value?.length > 0 ? Promise.resolve() : Promise.reject('请上传音频文件')
    }]
  };
  //提交添加或编辑声音的方法
  const handleOk = () => {
    formRef.value.validate().then(async () => {
      confirmLoading.value = true;
      try {
        // 根据是否有ID判断是添加还是编辑操作
        if (form.voiceId) {
          // 编辑前进行数据校验
          if (!form.voiceName || form.voiceName.length < 2 || form.voiceName.length > 8) {
            message.error('声音名称长度必须在2到8个字符之间',1);
            return;
          }
          if (!form.voiceNote) {
            message.error('请输入声音备注内容',1);
            return;
          }
     
          await editVoiceData(form.voiceId, {
            voiceName: form.voiceName,
            voiceNote: form.voiceNote
          });
          message.success('编辑成功',1);
          modalVisible.value = false;
        } else {
          message.loading('正在添加声音，请稍候...', 0);
          const formData = new FormData();
          // 获取上传的文件
          const uploadFile = form.file[0]?.originFileObj;
          formData.append('voiceName', form.voiceName);
          formData.append('note', form.voiceNote);
          formData.append('file', uploadFile);
          const res = await addVoice(formData);
          const taskId = res.data.taskId;
      
          //    轮询克隆任务进度
          const MAX_RETRIES = 60; // 最大重试次数（1分钟）
          const TIMEOUT = 60000; // 超时时间（60秒）
          const MIN_INTERVAL = 500; // 最小轮询间隔（0.5秒）
          const MAX_INTERVAL = 2000; // 最大轮询间隔（2秒）
      
          let progress = 0;
          let retryCount = 0;
          const startTime = Date.now();
      
          while (progress < 100 && retryCount < MAX_RETRIES) {
            try {
              const statusRes = await getCloneStatus(taskId);
              progress = statusRes.data.progress;
      
              if (progress === 100) {
                message.destroy();
                message.success('添加成功',1);
                modalVisible.value = false;
                break;
              }
      
              // 检查是否超时
              if (Date.now() - startTime > TIMEOUT) {
                message.destroy();
                throw new Error('克隆任务超时');
              }
      
              // 根据进度动态调整轮询间隔
              const interval = Math.min(
                MAX_INTERVAL,
                Math.max(MIN_INTERVAL, 1000 - progress * 5)
              );
              await new Promise(resolve => setTimeout(resolve, interval));
              retryCount++;
            } catch (error) {
              message.destroy();
              message.error('克隆声音失败',1 );
              break;
            }
          }
      
          if (progress < 100) {
            message.destroy();
            message.error('克隆声音未完成，请稍后重试',1);
          }
        }
        modalVisible.value = false;
        // 重新获取列表数据
        await fetchVoiceList(activeType.value, current.value, pageSize.value, searchQuery.value);
        resetForm();
      } catch (error) {
        message.destroy();
        message.error('操作失败',1);
      } finally {
        confirmLoading.value = false;
      }
    }).catch(err => {
      console.log('Error:', err);
    });
  };
  
  // 上传前校验
  const beforeUpload = (file) => {
    const allowedTypes = ['audio/mp3', 'audio/mpeg', 'audio/wav'];
    const isValidType = allowedTypes.includes(file.type);
    const isValidSize = file.size / 1024 / 1024 < 50;

    if (!isValidType) {
      message.error('只能上传 MP3 或 WAV 格式的音频文件!',1);
      return false;
    }

    if (!isValidSize) {
      message.error('文件大小不能超过 50MB!',1);
      return false;
    }

    // 通过校验，允许添加到文件列表
    return false;
  };
</script>
<style lang=scss scoped>
  .tts-container {
    background-color: #f3f4f6;

  }

  .voice-container {
    height: 90.1vh;
    max-width: 90rem;
    margin: 0 auto;
    padding: 24px 32px;

  }

  .header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 32px;

    .title {
      font-size: 24px;
      font-weight: 540;
      color: #1f2937;
    }

    .search-area {
      display: flex;
      align-items: center;
      gap: 16px;

      .search-input {
        width: 280px;
      }
    }
  }

  .filter-bar {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 32px;
    overflow-x: auto;
    padding-bottom: 8px;
  }

  .table-card {
    margin-bottom: 24px;
    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03), 0 1px 6px -1px rgba(0, 0, 0, 0.02), 0 2px 4px 0 rgba(0, 0, 0, 0.02);
  }

  .play-action {
    display: flex;
    justify-content: center;
  }

  .action-group {
    display: flex;
    justify-content: center;
    gap: 8px;
  }

  .action-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
  }

  .play-icon {
    color: #9ca3af;
    font-size: 18px;

    &:hover {
      color: #1890ff;
    }
  }

  .edit-icon {
    color: #9ca3af;
    font-size: 18px;

    &:hover {
      color: #1890ff;
    }
  }

  .delete-icon {
    color: #9ca3af;
    font-size: 18px;

    &:hover {
      color: #f5222d;
    }
  }

  .pagination-area {
    display: flex;
    align-items: center;
    justify-content: space-between;

    .total-info {
      font-size: 14px;
      color: #212224;
      border: 1px solid #e5e7eb;
      padding: 8px 16px;
      border-radius: 8px;
      background-color: #fafafa;
    }
  }

  .help-icon {
    color: #9ca3af;
    font-size: 1.125rem;
  }

  .upload-icon {
    font-size: 24px;
  }

  .upload-tip {
    font-size: 12px;
    color: #6b7280;
  }

  .add-button,
  .filter-button {
    border-radius: 8px;
    white-space: nowrap;
  }

  :deep(.ant-upload-drag) {
    background: white;
  }

  :deep(.ant-input-search-button) {
    border-radius: 0 8px 8px 0 !important;
  }

  :deep(.ant-pagination-item) {
    border-radius: 8px !important;
  }

  :deep(.ant-pagination-prev .ant-pagination-item-link),
  :deep(.ant-pagination-next .ant-pagination-item-link) {
    border-radius: 8px !important;
  }

  :global(.ant-modal-content) {
    margin-top: 15vh !important;
  }

  :deep(.ant-input-textarea-show-count::after) {
    display: block;
    color: rgba(0, 0, 0, 0.45);
    font-size: 14px;
  }

  :deep(.help-icon) {
    margin-left: 4px;
    color: rgba(0, 0, 0, 0.45);
    font-size: 14px;
    cursor: help;
  }
</style>